﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using EnvDTE;
using EnvDTE80;
using Expect.Core;
using Microsoft.VisualStudio.Shell;

namespace Expect.VisualStudio.Extensions.Commands.Handlers
{    
    class CreateFixtureClass : UnresolvedFixtureCommand
    {
        public CreateFixtureClass(IExpectPackage pkg)
            : base(pkg, CommandIDs.cmdidCreateFixtureClass)
        {
        }

        public CreateFixtureClass(IExpectPackage pkg, int id)
            : base(pkg, id)
        {
        }

        public override string DisplayText
        {
            get
            {
                return "Fixture class => create fixture class";
            }
        }

        protected virtual Type GetClassType()
        {
            return typeof(AbstractFixture);
        }

        public override void ExecuteCommand(SelectionResolution resolution)
        {         
            IExpectVSService expectService = (IExpectVSService)GetVSService(typeof(SExpectVSService));
            var dte = (DTE2)GetVSService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE));
            
            var fixtureclass = GenerateFixtureClass(resolution, dte, GetClassType());

            if (fixtureclass == null) return;

            ProcessClass(resolution, fixtureclass);

            fixtureclass.ProjectItem.Open().Activate();

            /*var funcedit = function.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint();
            funcedit.ReplaceText("public".Length, "public override", 0);*/
        }

        protected virtual void ProcessClass(SelectionResolution resolution, CodeClass generatedclass)
        {
            CodeFunction2 function = (CodeFunction2)generatedclass.AddFunction("RunRow", vsCMFunction.vsCMFunctionFunction, "bool?", -1, vsCMAccess.vsCMAccessPublic);
            function.OverrideKind = vsCMOverrideKind.vsCMOverrideKindOverride;
        }
        
        public CodeClass GenerateFixtureClass(SelectionResolution resolution, EnvDTE80.DTE2 dte, Type baseType)
        {
            var fixtureClassName = resolution.NameProvider.ElementName(resolution.Fixture.Rows[0].NamesForFluent().ToArray());
            var fixtureClassArgs = resolution.Fixture.Rows[0].ArgsForFluent().ToArray();
            var prjnamespace = resolution.Context.Item.ContainingProject.Properties.Item("RootNamespace").Value.ToString();
            var prjdir = Path.GetDirectoryName(resolution.Context.Item.ContainingProject.FileName);
            var fixtureFileName = Path.Combine(Path.Combine(prjdir, "fixtures"), fixtureClassName + ".cs");

            var project = resolution.Context.Item.ContainingProject;
            if (!project.ProjectItems.HasFolder("fixtures"))
            {
                project.ProjectItems.AddFolder("Fixtures");
            }
            prjnamespace = prjnamespace + ".Fixtures";

            var fixtureFullName = prjnamespace + "." + fixtureClassName;
            Dictionary<string, List<ProjectItem>> classes = new Dictionary<string, List<ProjectItem>>();
            resolution.Context.Item.ContainingProject.GetProjectItemForClassName(classes, fixtureFullName);
            if (classes.Count > 0)
            {
                ShowMessage("Enable to create the fixture class " + fixtureFullName + " because a class with this full name already exists");
                return null;
            }
            if (File.Exists(fixtureFileName))
            {
                ShowMessage("A file with path " + fixtureFileName + " already exists");
                return null;
            }

            CodeModel cm = resolution.Context.Item.ContainingProject.CodeModel;
            CodeNamespace currentnamespace = cm.AddNamespace(prjnamespace, fixtureFileName, -1);
            var edit = currentnamespace.GetStartPoint().CreateEditPoint();
            edit.Insert("using System;\r\n");
            edit.Insert("using Expect.Core;\r\n");

            object[] bases = { baseType.FullName };
            object[] interfaces = { /*"System.IDisposable", "System.IComparable"*/ };

            CodeClass generatedClass = (CodeClass)currentnamespace.AddClass(fixtureClassName, -1, bases, interfaces, vsCMAccess.vsCMAccessPublic);

            generatedClass.AddAttribute(typeof(FixtureHintAttribute).Name.Replace("Attribute", ""), "\"" + resolution.Fixture.Rows[0].ToHint() + "\"", -1);

            GenerateConstructor(resolution, fixtureClassName, generatedClass, fixtureClassArgs);
            //var endclass = generatedClass.GetEndPoint(vsCMPart.vsCMPartBody);
            //endclass.CreateEditPoint().Insert("//test\r\n");)

            return generatedClass;
        }

        protected virtual void GenerateConstructor(SelectionResolution resolution, string fixtureClassName, CodeClass generatedClass, string[] fixtureClassArgs)
        {
            if (fixtureClassArgs.Length > 0)
            {
                var ctor = (CodeFunction2)generatedClass.AddFunction(fixtureClassName, vsCMFunction.vsCMFunctionConstructor, null, -1, vsCMAccess.vsCMAccessPublic);
                
                foreach (var arg in fixtureClassArgs)
                {
                    ctor.AddParameter("param_for_" + resolution.NameProvider.ElementName(arg), "string", ctor.Parameters.Count);
                }
            }
        }
    }
}
